package bookhive.client;

import net.jxta.id.IDFactory;
import net.jxta.impl.util.AdvCooker;
import net.jxta.discovery.DiscoveryService;
import net.jxta.document.AdvertisementFactory;
import net.jxta.pipe.OutputPipe;
import net.jxta.pipe.PipeID;
import net.jxta.platform.ModuleClassID;
import net.jxta.platform.ModuleSpecID;
import net.jxta.peergroup.PeerGroup;
import net.jxta.protocol.ModuleSpecAdvertisement;
import net.jxta.protocol.PipeAdvertisement;
import net.jxta.document.Advertisement;
import net.jxta.endpoint.Message;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.util.Enumeration;
import java.util.Properties;
import java.util.TreeSet;
import java.util.Vector;

import org.jopac2.jbal.RecordInterface;
import org.jopac2.utils.BookSignature;
import org.jopac2.utils.JOpac2Exception;
import org.jopac2.utils.Parametro;


import bookhive.LibraryPeer;
import bookhive.datasource.DataSource;
import bookhive.datastructures.datasource.DataItem;
import bookhive.datastructures.datasource.HashItem;
import bookhive.service.ServicePipeBuilder;
import bookhive.utils.MessageCreator;

public class ClientPeer extends LibraryPeer {

	protected ModuleSpecAdvertisement msAdv;

	protected PipeAdvertisement opipeAdv;

	protected OutputPipe opipe = null;

	protected PipeAdvertisement inPipeAdv;

	public TreeSet<DataItem> currentDataResults = new TreeSet<DataItem>();

	public TreeSet<HashItem> currentHashResults = new TreeSet<HashItem>();

	private ResultListener resultListener = null;

	private String currentHashQuery;

	private DataSource dataSource;

	public ClientPeer(PeerGroup pg) {
		super(pg);
		init();
	}

	public ClientPeer(PeerGroup pg, ResultListener rl) {
		super(pg);
		resultListener = rl;
		init();
	}

	protected void createReponseInputPipe() {

		try {
			ClientResponseHandler reponseHandler = new ClientResponseHandler(
					this);
			inPipeAdv = ServicePipeBuilder.createResponsePipe(group,
					"WorldCatalog:ResponseInputPipe", reponseHandler, 3000);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void init() {
		File f = new File("config/clientcatalog.config");
		Properties p = new Properties();
		try {
			p.load(new FileInputStream(f));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

		setCatalogProperties(p);

		jxtaInit();
		createReponseInputPipe();
		currentHashQuery = "";
	}

	@SuppressWarnings("unchecked")
	@Override
	public void run() {
		String dataSourceClassName = catalogProperties
				.getProperty("DataSourceClass");
		Class dataSourceClass = null;
		try {
			dataSourceClass = Class.forName(dataSourceClassName);
		} catch (ClassNotFoundException e) {
		}
		if (dataSourceClass == null) {
			System.out.println("Class " + dataSourceClassName
					+ " not found. Exiting...");
			System.exit(1);
		}
		try {
			dataSource = (DataSource) dataSourceClass.newInstance();
		} catch (InstantiationException e) {
			System.out.println("Problem instantiating " + dataSourceClassName);
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.out.println("Illegal Access to " + dataSourceClassName);
			e.printStackTrace();
		}

	}

	private void jxtaInit() {

		if (group != null) {
			discovery = group.getDiscoveryService();
			discovery.remotePublish(group.getPeerAdvertisement());

			discovery.getRemoteAdvertisements(null, DiscoveryService.PEER,
					null, null, 10);
			try {
				Thread.sleep(5000);
				Enumeration<Advertisement> peers = discovery
						.getLocalAdvertisements(DiscoveryService.PEER, null,
								null);
				while (peers.hasMoreElements()) {
					peers.nextElement();
					// System.out.println("Found peerAdv " + adv.getID());
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
			pipes = group.getPipeService();
		} else {
			System.out.print("Group is null");
			return;
		}

		System.out.print("Seaching for collector's MSA...");
		if ((msAdv = findModuleSpecAdv()) == null) {
			System.out
					.println("..MSA not found, please check connection - terminating.");
		} else {
			System.out.println("..found collector's MSA");
			// System.out.println("Extracing pipe adv from MSA...");
			opipeAdv = msAdv.getPipeAdvertisement();
			System.out.println("- - - - - - - - - - - - - - - - - - - -");
		}
	}

	public void sendDataQuery(String hash) {
		TreeSet<String> ts = new TreeSet<String>();
		ts.add(hash);
		sendDataQuery(ts);
	}

	public void sendDataQuery(TreeSet<String> hashes) {
		Message m = MessageCreator.createDataQueryMessage(hashes, inPipeAdv);
		sendMessage(m);
	}

	public void sendHashQuery(TreeSet<Vector<Parametro>> attributesSet) {
		Message m = MessageCreator.createHashQueryMessage(attributesSet,
				inPipeAdv);
		sendMessage(m);
	}

	public void sendHashQuery(String hashquery) {
		currentHashQuery = hashquery;
		Message m = MessageCreator.createHashQueryMessage(hashquery, inPipeAdv);
		sendMessage(m);
	}

	public void sendUpdate(Vector<RecordInterface> ris) {
		if (ris.size() < 1)
			return;
		Message m = MessageCreator.createUpdateMessage(ris, inPipeAdv);
		sendMessage(m);
	}

	protected void sendMessage(Message msg) {
		if (opipeAdv == null) {
			jxtaInit();
			return;
		}
		try {
			if (opipe == null || opipe.isClosed()) {
				opipe = pipes.createOutputPipe(opipeAdv, 100000);
			}
			opipe.send(msg);
		} catch (Exception e) {
		}// e.printStackTrace(); }
	}

	public PipeAdvertisement createInputPipeAdvIfNotExist() {
		Advertisement padv = null;
		if ((padv = findPipeAdv()) != null) {
			// found it
			return (PipeAdvertisement) padv;
		}
		System.out
				.println("Previously published pipe advertisment not found, creating a new one.");
		// create a new one
		PipeAdvertisement pipeAdv;
		pipeAdv = (PipeAdvertisement) AdvertisementFactory
				.newAdvertisement(PipeAdvertisement.getAdvertisementType());
		pipeAdv.setName(servicePipeName);
		pipeAdv.setType(servicePipeType);
		PipeID tpID = IDFactory.newPipeID(group.getPeerGroupID());
		pipeAdv.setPipeID(tpID);

		System.out.println("Locally caching the new pipe advertsiement...");
		try {
			discovery.publish(pipeAdv);
		} catch (Exception ex) {
			System.out.println("Err: " + ex);
		}
		return pipeAdv;
	}

	protected void publishModuleSpecAdv(PipeAdvertisement ServicePipeAdv) {

		ModuleSpecAdvertisement msAdv = null;
		System.out.println("Creating new MSA");

		ModuleClassID mcid = IDFactory.newModuleClassID();
		ModuleSpecID msid = IDFactory.newModuleSpecID(mcid);

		if (msid != null) {
			msAdv = AdvCooker.buildModuleSpecAdvertisement(msid, // ModuleSpecID
																	// msid,
					ModuleSpecName, // String moduleSpecName,
					"Catalog engine", // String moduleSpecDescription,
					"SIB-SBA Units.it", // String creator,
					"Version 1.0", // String version,
					"http://www.biblio.units.it", // String specURI,
					ServicePipeAdv, // PipeAdvertisement pipeAdv,
					null, // ModuleSpecID proxySpecID,
					null, // ModuleSpecID authorizationSpecID,
					null // StructuredDocument param
					);
			System.out.println("Locally and remotely publish the MSA");
			try {
				discovery.publish(msAdv);
				discovery.remotePublish(msAdv, DiscoveryService.ADV);
			} catch (Exception ex) {
				System.out.println("Err: " + ex);
			}
		}
	}

	public synchronized void addDataResults(TreeSet<DataItem> dataItems) {
		currentDataResults.addAll(dataItems);
		if (resultListener != null)
			resultListener.newDataRecords(dataItems);
	}

	public synchronized void addHashResults(TreeSet<HashItem> hashItems) {
		currentHashResults.addAll(hashItems);
		if (resultListener != null)
			resultListener.newHashRecords(hashItems);
	}

	public void sendUpdate(String hash, Vector<BookSignature> signs) {
		// TODO Auto-generated method stub

	}

	public synchronized String getCurrentHashQuery() {
		return currentHashQuery;

	}

	@SuppressWarnings("unchecked")
	public void initDataSource() {
		String dataSourceClassName = catalogProperties
				.getProperty("DataSourceClass");
		Class dataSourceClass = null;
		try {
			dataSourceClass = Class.forName(dataSourceClassName);
		} catch (ClassNotFoundException e) {
		}
		if (dataSourceClass == null) {
			System.out.println("Class " + dataSourceClassName
					+ " not found. Exiting...");
			System.exit(1);
		}
		try {
			dataSource = (DataSource) dataSourceClass.newInstance();
		} catch (InstantiationException e) {
			System.out.println("Problem instantiating " + dataSourceClassName);
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			System.out.println("Illegal Access to " + dataSourceClassName);
			e.printStackTrace();
		}
	}

	public void addBookSignature(String hash, BookSignature bs) {
		System.out.println("Aggiungi BookSignature:");
		System.out.println(bs);

		String queryData = "hash=" + hash + "&LID=" + bs.getLibraryId()
				+ "&LNA=" + bs.getLibraryName() + "&BNO=" + bs.getBookNumber()
				+ "&BLO=" + bs.getBookLocalization() + "&BCO="
				+ bs.getBookCons();

		dataSource.sendUpdateQuery(queryData);
	}

	public void addRecord(RecordInterface ri) throws NoSuchAlgorithmException,
			JOpac2Exception {
		System.out.println("Aggiungi Record:");
		System.out.println(ri);

		String queryData = "hash=" + ri.getHash() + "&data=" + ri.toString()
				+ "&type=" + ri.getTipo();

		dataSource.sendRedundQuery(queryData);
	}

}